#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
#include "common.h"
#include "ast.h"
#include "internal.h"

/* Translation Unit */

struct translationunit *con_tu_init()
{
	struct translationunit *tu;

	tu = malloc(sizeof(struct translationunit));
	T_ERROR_MALLOC(tu);

	tu->e = con_e_list_init();
	tu->v = con_v_list_init();
	tu->f = con_f_list_init();

	return tu;
}

struct translationunit *con_tu_add_enum(struct translationunit *tu, struct e_enumerate *e)
{
	tu->e = con_e_list_add(tu->e, e);
	return tu;
}

struct translationunit *con_tu_add_var(struct translationunit *tu, struct v_list *v)
{
	tu->v = con_v_list_combine(tu->v, v);
	return tu;
}

struct translationunit *con_tu_add_event(struct translationunit *tu, struct f_function *f)
{
	tu->f = con_f_list_add(tu->f, f);
	return tu;
}

struct translationunit *con_tu_add_function(struct translationunit *tu, struct f_function *f)
{
	tu->f = con_f_list_add(tu->f, f);
	return tu;
}

void free_translationunit(struct translationunit *tu)
{
	/* TODO */
}

/* Identifier */

struct i_list *con_i_list_init(char *i)
{
	T_LIST_INIT_ADD(struct i_list, struct i_node, identifier, i);
}

struct i_list *con_i_list_add(struct i_list *l, char *i)
{
	T_LIST_ADD(l, struct i_node, identifier, i);
}

/* Enumerate */

struct e_enumerate *con_e_base(char *i, struct e_elemlist *ml)
{
	struct e_enumerate *e;

	e = malloc(sizeof(struct e_enumerate));
	T_ERROR_MALLOC(e);

	e->identifier = i;
	e->cate = E_BASE;
	e->spec.base.elemlist = ml;

	return e;
}

struct e_enumerate *con_e_comb(char *i, struct i_list *il)
{
	struct e_enumerate *e;

	e = malloc(sizeof(struct e_enumerate));
	T_ERROR_MALLOC(e);

	e->identifier = i;
	e->cate = E_COMB;
	e->spec.comb.identifiers = il;

	return e;
}

struct e_elemlist *con_e_elemlist_init(struct e_element *m)
{
	T_LIST_INIT_ADD(struct e_elemlist, struct e_elemnode, element, m);
}

struct e_elemlist *con_e_elemlist_add(struct e_elemlist *l, struct e_element *m)
{
	T_LIST_ADD(l, struct e_elemnode, element, m);
}

struct e_element *con_e_elem(char *i, int c, char *r)
{
	struct e_element *m;

	m = malloc(sizeof(struct e_element));
	T_ERROR_MALLOC(m);

	m->identifier = i;
	m->value = c;
	m->desc = r;

	return m;
}

struct e_list *con_e_list_init()
{
	T_LIST_INIT(struct e_list);
}

struct e_list *con_e_list_add(struct e_list *l, struct e_enumerate *e)
{
	T_LIST_ADD(l, struct e_node, enumerate, e);
}

/* Variable */

struct v_variable *con_v_variable(struct t_type *t, char *i)
{
	struct v_variable *v;

	v = malloc(sizeof(struct v_variable));
	T_ERROR_MALLOC(v);

	v->identifier = i;
	v->type = t;
	t->refcount++;

	return v;
}

struct v_list *con_v_list_init()
{
	T_LIST_INIT(struct v_list);
}

struct v_list *con_v_list_add(struct v_list *l, struct v_variable *v)
{
	T_LIST_ADD(l, struct v_node, variable, v);
}

struct v_list *con_v_list_combine(struct v_list *vl1, struct v_list *vl2)
{
	T_LIST_COMBINE(vl1, vl2, struct v_node);
}

struct v_list *con_v_list_create(struct t_type *t, struct i_list *il)
{
	struct v_list *vl;
	struct v_variable *v;
	struct i_node *in;

	vl = con_v_list_init();

	for (in = il->first; in != NULL; in = in->next) {
		v = con_v_variable(t, in->identifier);
		vl = con_v_list_add(vl, v);
	}

	return vl;
}

/* Function */

struct f_function *con_f_event(char *i, struct v_list *v, struct s_list *s)
{
	struct f_function *f;

	f = malloc(sizeof(struct f_function));
	T_ERROR_MALLOC(f);

	f->cate = F_EVENT;

	f->identifier = i;
	f->variables = v;
	f->statements = s;

	return f;
}

struct f_function *con_f_function(struct t_type *t, char *i, struct v_list *v1, struct v_list *v2, struct s_list *s)
{
	struct f_function *f;

	f = malloc(sizeof(struct f_function));
	T_ERROR_MALLOC(f);

	f->cate = F_FUNCTION;

	f->identifier = i;
	f->variables = v2;
	f->statements = s;

	f->spec.function.returntype = t;
	t->refcount++;
	f->spec.function.arguments = v1;

	return f;
}

struct f_list *con_f_list_init()
{
	T_LIST_INIT(struct f_list);
}

struct f_list *con_f_list_add(struct f_list *l, struct f_function *f)
{
	T_LIST_ADD(l, struct f_node, function, f);
}

/* Statement */

struct s_list *con_s_list_init()
{
	T_LIST_INIT(struct s_list);
}

struct s_list *con_s_list_add(struct s_list *l, struct s_statement *s)
{
	T_LIST_ADD(l, struct s_node, statement, s);
}

struct s_statement *con_s_expression(struct x_expression *x)
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_EXPRESSION;

	s->spec.expression.expression = x;

	return s;
}

struct s_statement *con_s_if(struct x_expression *x, struct s_list *sl)
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_IF;

	s->spec.s_if.condition = x;
	s->spec.s_if.statements = sl;

	return s;
}

struct s_statement *con_s_ifelse(struct x_expression *x, struct s_list *sl1, struct s_list *sl2)
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_IF_ELSE;

	s->spec.s_if_else.condition = x;
	s->spec.s_if_else.truestatements = sl1;
	s->spec.s_if_else.falsestatements = sl2;

	return s;
}

struct s_statement *con_s_while(struct x_expression *x, struct s_list *sl)
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_WHILE;

	s->spec.s_while.condition = x;
	s->spec.s_while.statements = sl;

	return s;
}

struct s_statement *con_s_dowhile(struct s_list *sl, struct x_expression *x)
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_DO_WHILE;

	s->spec.s_do_while.condition = x;
	s->spec.s_do_while.statements = sl;

	return s;
}

struct s_statement *con_s_for(struct x_expression *x1, struct x_expression *x2, struct x_expression *x3, struct s_list *sl)
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_FOR;

	s->spec.s_for.initialization = x1;
	s->spec.s_for.condition = x2;
	s->spec.s_for.iteration = x3;
	s->spec.s_for.statements = sl;

	return s;
}

struct s_statement *con_s_break()
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_BREAK;

	return s;
}

struct s_statement *con_s_continue()
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_CONTINUE;

	return s;
}

struct s_statement *con_s_return(struct x_expression *x)
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_RETURN;

	s->spec.s_return.value = x;

	return s;
}

struct s_statement *con_s_call(char *i)
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_CALL;

	s->spec.s_call.eventidentifier = i;

	return s;
}

struct s_statement *con_s_trans(char *i)
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_TRANS;

	s->spec.s_trans.eventidentifier = i;

	return s;
}

struct s_statement *con_s_input(struct x_expression *screen, struct x_expression *destination)
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_INPUT;

	s->spec.s_input.screen = screen;
	s->spec.s_input.destination = destination;

	return s;
}

struct s_statement *con_s_output(struct x_expression *screen, char *string)
{
	struct s_statement *s;

	s = malloc(sizeof(struct s_statement));
	T_ERROR_MALLOC(s);

	s->cate = S_OUTPUT;

	s->spec.s_output.screen = screen;
	s->spec.s_output.string = string;

	return s;
}

/* Type Expression */

struct t_type *con_t_int()
{
	struct t_type *t;

	t = malloc(sizeof(struct t_type));
	T_ERROR_MALLOC(t);

	t->cate = T_INTEGER;
	t->refcount = 0;

	return t;
}

struct t_type *con_t_bool()
{
	struct t_type *t;

	t = malloc(sizeof(struct t_type));
	T_ERROR_MALLOC(t);

	t->cate = T_BOOLEAN;
	t->refcount = 0;

	return t;
}

struct t_type *con_t_enum(char *i)
{
	struct t_type *t;

	t = malloc(sizeof(struct t_type));
	T_ERROR_MALLOC(t);

	t->cate = T_ENUMERATE;
	t->refcount = 0;

	t->spec.enumerate.identifier = i;

	return t;
}

struct t_type *con_t_cont(struct t_type *t2)
{
	struct t_type *t;

	t = malloc(sizeof(struct t_type));
	T_ERROR_MALLOC(t);

	t->cate = T_CONTAINER;
	t->refcount = 0;
	t2->refcount++;

	t->spec.container.elemtype = t2;

	return t;
}

struct t_type *con_t_array_int(struct t_type *t2, struct x_expression *x)
{
	struct t_type *t;

	t = malloc(sizeof(struct t_type));
	T_ERROR_MALLOC(t);

	t->cate = T_ARRAY_INT;
	t->refcount = 0;
	t2->refcount++;

	t->spec.array_int.elemtype = t2;
	t->spec.array_int.size = x;

	return t;
}

struct t_type *con_t_array_enum(struct t_type *t2, char *i)
{
	struct t_type *t;

	t = malloc(sizeof(struct t_type));
	T_ERROR_MALLOC(t);

	t->cate = T_ARRAY_ENUM;
	t->refcount = 0;
	t2->refcount++;

	t->spec.array_enum.elemtype = t2;
	t->spec.array_enum.enumerate = i;

	return t;
}

/* Expression */

struct x_expression *con_x_un(enum x_unop xo, struct x_expression *x2)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_UNARY;

	x->spec.unary.oper = xo;
	x->spec.unary.opnd = x2;

	return x;
}

struct x_expression *con_x_bin(enum x_binop xo, struct x_expression *x1, struct x_expression *x2)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_BINARY;

	x->spec.binary.oper = xo;
	x->spec.binary.left = x1;
	x->spec.binary.right = x2;

	return x;
}

struct x_expression *con_x_tri(enum x_triop xo, struct x_expression *x1, struct x_expression *x2, struct x_expression *x3)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_TRINARY;

	x->spec.trinary.oper = xo;
	x->spec.trinary.first = x1;
	x->spec.trinary.second = x2;
	x->spec.trinary.third = x3;

	return x;
}

struct x_expression *con_x_id(char *i)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_IDENTIFIER;

	x->spec.identifier.identifier = i;

	return x;
}

struct x_expression *con_x_bool(bool b)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_BOOLEAN;

	x->spec.boolean.boolean = b;

	return x;
}

struct x_expression *con_x_int(int c)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_INTEGER;

	x->spec.integer.integer = c;

	return x;
}

struct x_expression *con_x_call(char *i, struct x_list *xl)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_CALL;

	x->spec.call.function = i;
	x->spec.call.arguments = xl;

	return x;
}

struct x_expression *con_x_array(char *i, struct x_expression *x2)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_ARRAY;

	x->spec.array.array = i;
	x->spec.array.index = x2;

	return x;
}

struct x_expression *con_x_ds_random(struct t_type *t)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_DS;

	x->spec.ds.oper = X_RANDOM;
	x->spec.ds.spec.random.type = t;
	t->refcount++;

	return x;
}

struct x_expression *con_x_ds_value(struct x_expression *enumerate)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_DS;

	x->spec.ds.oper = X_VALUE;
	x->spec.ds.spec.value.enumerate = enumerate;

	return x;
}

struct x_expression *con_x_ds_size(struct x_expression *container)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_DS;

	x->spec.ds.oper = X_SIZE;
	x->spec.ds.spec.size.container = container;

	return x;
}

struct x_expression *con_x_ds_fill(struct x_expression *container)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_DS;

	x->spec.ds.oper = X_FILL;
	x->spec.ds.spec.fill.container = container;

	return x;
}

struct x_expression *con_x_ds_shuffle(struct x_expression *container)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_DS;

	x->spec.ds.oper = X_SHUFFLE;
	x->spec.ds.spec.shuffle.container = container;

	return x;
}

struct x_expression *con_x_ds_peek(struct x_expression *container, struct p_position *position)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_DS;

	x->spec.ds.oper = X_PEEK;
	x->spec.ds.spec.peek.container = container;
	x->spec.ds.spec.peek.position = position;

	return x;
}

struct x_expression *con_x_ds_remove(struct x_expression *container, struct p_position *position)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_DS;

	x->spec.ds.oper = X_REMOVE;
	x->spec.ds.spec.remove.container = container;
	x->spec.ds.spec.remove.position = position;

	return x;
}

struct x_expression *con_x_ds_insert(struct x_expression *container, struct p_position *position, struct x_expression *element)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_DS;

	x->spec.ds.oper = X_INSERT;
	x->spec.ds.spec.insert.container = container;
	x->spec.ds.spec.insert.position = position;
	x->spec.ds.spec.insert.element = element;

	return x;
}

struct x_expression *con_x_ds_extract(struct x_expression *enumerate, struct x_expression *index)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_DS;

	x->spec.ds.oper = X_EXTRACT;
	x->spec.ds.spec.extract.enumerate = enumerate;
	x->spec.ds.spec.extract.index = index;

	return x;
}

struct x_expression *con_x_ds_combine(struct x_list *components)
{
	struct x_expression *x;

	x = malloc(sizeof(struct x_expression));
	T_ERROR_MALLOC(x);

	x->cate = X_DS;

	x->spec.ds.oper = X_COMBINE;
	x->spec.ds.spec.combine.components = components;

	return x;
}

struct x_list *con_x_list_init()
{
	T_LIST_INIT(struct x_list);
}

struct x_list *con_x_list_add(struct x_list *l, struct x_expression *x)
{
	T_LIST_ADD(l, struct x_node, expression, x);
}

/* Position */

struct p_position *con_p_top()
{
	struct p_position *p;

	p = malloc(sizeof(struct p_position));
	T_ERROR_MALLOC(p);

	p->cate = P_TOP;

	return p;
}

struct p_position *con_p_bottom()
{
	struct p_position *p;

	p = malloc(sizeof(struct p_position));
	T_ERROR_MALLOC(p);

	p->cate = P_BOTTOM;

	return p;
}

struct p_position *con_p_random()
{
	struct p_position *p;

	p = malloc(sizeof(struct p_position));
	T_ERROR_MALLOC(p);

	p->cate = P_RANDOM;

	return p;
}

struct p_position *con_p_expression(struct x_expression *x)
{
	struct p_position *p;

	p = malloc(sizeof(struct p_position));
	T_ERROR_MALLOC(p);

	p->cate = P_EXPRESSION;

	p->spec.expression.expression = x;

	return p;
}

